import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * This is a helper class for the RocketAnalysisTool.
 */
public class RocketHelper 
{
	private int headerWidth;
	/**
	 * The width in characters that headers should be.(boxes with headings)
	 * @param headerWidth - The number of characters wide for a headerBox
	 */
	public RocketHelper(int headerWidth)
	{
		this.headerWidth = headerWidth;
	}
	
	public Version newVersion(int releaseNumber)
	{
		Date releaseDate = null;
		String versionID = "", versionReleaseDate;
		
		// Obtain Version ID from user
		System.out.println();
		RocketTools.writeHeader("Add Version", headerWidth);
		
		System.out.print("Enter unique Version identifier: ");
		versionID = RocketTools.readInput();
		
		// Obtain Version release date from user
		System.out.print("Enter the Release Date DD/MM/YYYY : ");
		versionReleaseDate = RocketTools.readInput(); 
		releaseDate = RocketTools.processDate(versionReleaseDate);		
		
		//Obtains version class/Jar files listing from user
		LinkedList<String> sourceList = new LinkedList<String>();
		while (true)
		{
			System.out.print("Enter source class or jar file: ");
			String sourceFile = RocketTools.readInput().trim(); 
						
			if (sourceFile != null)
			{
				if(sourceFile.endsWith(".jar"))
					sourceList = RocketTools.extractJar(sourceFile, sourceList);
				else
					sourceList.add(sourceFile);
			}
				
			System.out.print("Enter another class or jar file (Y/N): ");
			String yesNoInput = RocketTools.readInput();
			
			if (!yesNoInput.equalsIgnoreCase("y"))
				break;		
		}
		
		// Set persistent data 
		Version version = new Version(versionID, releaseDate);
		version.setSourceList(sourceList);
		version.setReleaseSequenceNum(releaseNumber);		

		return version;
	}
	
	public void analyseVersion(Version version)
	{
		System.out.println();
		RocketTools.writeHeader("Analyse Version", headerWidth);
		
		Hashtable<String, ClassMetricExtractor> extractors = new Hashtable<String, ClassMetricExtractor>();
		
		// Loop through each source file and analyse
		for(String sourceFile : version.getSourceList())
		{
			System.out.println("Analysing: " + sourceFile);
			
			try 
			{ 
				ClassMetricExtractor cme = new ClassMetricExtractor(new FileInputStream(sourceFile));
				extractors.put(cme.className, cme);
				
				MetricClass metricClass = new MetricClass();
				metricClass.setFanOutClasses(cme.dependencies);
				metricClass.setFanOutCount(cme.fanOutCount);
				metricClass.setName(cme.className);
				metricClass.setSuperClassCount(cme.superClassCount);
				metricClass.setSuperClassName(cme.superClassName);
				metricClass.setFieldCount(cme.fieldCount);
				metricClass.setMethodCount(cme.methodCount);
				metricClass.setPublicMethodCount(cme.publicMethodCount);
				metricClass.setPrivateMethodCount(cme.privateMethodCount);
				metricClass.setProtectedMethodCount(cme.protectedMethodCount);
				
				version.addMetricClass(metricClass);							
			}
			catch(IOException e)
			{
				System.out.println(e);
				/*Remove evil catch block */ 
			}			
		}
		
		//Calculate the fan in for each extractor
		calculateFanIn(extractors);
		
		// Update fan in.
		Enumeration e = extractors.elements();
		while (e.hasMoreElements())
		{
//			ClassMetricExtractor cme = (ClassMetricExtractor)e.nextElement();
//			Hashtable<String, String> table = cme.getFanIn();
//			Enumeration e2 = table.elements();
//			
//			while (e2.hasMoreElements())
//			{
//				System.out.println(e.nextElement());
//			}
			ClassMetricExtractor cme = (ClassMetricExtractor)e.nextElement();
			MetricClass metricClass = version.getMetricClass(cme.className);
			metricClass.setFanIn(cme.getFanInClasses());			
			metricClass.setFanInCount(cme.getFanInClasses().size());			
		}
	}
	

	private void calculateFanIn(Hashtable<String, ClassMetricExtractor> extractors)
	{
		Enumeration e = extractors.elements();
		while (e.hasMoreElements())
		{
			ClassMetricExtractor cme = (ClassMetricExtractor)e.nextElement();
			
			LinkedList<String> fanOutNodes = new LinkedList<String>(cme.dependencies);
						
			for(String fanOutNode : fanOutNodes)
			{
				try
				{
					ClassMetricExtractor cmeIn = extractors.get(fanOutNode);				
					cmeIn.addFanIn(cme.className);
				}
				catch(NullPointerException err) {}
			}							    	
		}		
	}
	
	/**
	 * Displays the fanIn data for the specified ClassMetricExtractor
	 * @param cme - the ClassMetricExtractor.
	 */
	private void displayFanIn(ClassMetricExtractor cme)
	{
		System.out.println("Printing FanIn for class: " + cme.className);
		LinkedList<String> fanInClasses = cme.getFanInClasses();
		Iterator i = fanInClasses.iterator();
		
		while (i.hasNext())		
			System.out.println(i.next());		
	}
	

	public void save(Project project)
	{				
		// Uses currentProject name as fileName
		String fileName = project.getName().concat(".xml"); 
		save(project, fileName);
	}
	
	public void save(Project project, String fileName)
	{
		System.out.println();
		RocketTools.writeHeader("Save Project", headerWidth);
		
		try
		{					
			// Serialize Project to XML string
			XStream xStream = new XStream(new DomDriver());			
			String xmlData = xStream.toXML(project);
			// Write XML to file
			PrintWriter pw = new PrintWriter(new FileOutputStream(fileName));
			pw.write(xmlData);
			pw.close();
			
			System.out.println("\nProject saved to: " + fileName +"\n");
		} 
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
	}
	
	/**
	 * Prompts the user to specify the path to a RAT project file and loads
	 * the project into memory.
	 * 
	 * @return - The loaded project
	 * @throws - FileNotFoundException 
	 */
	public Project load()
	{
		Project toLoad = null;
		
		System.out.println();
		RocketTools.writeHeader("Load Project", headerWidth);
		
		System.out.print("Enter the project file location: ");
		String projectFilePath = RocketTools.readInput().trim(); 
		
		try
		{
			XStream xStream = new XStream(new DomDriver());
			toLoad = (Project)xStream.fromXML(new FileInputStream(projectFilePath));
		}
		catch(FileNotFoundException e) 
		{ 
			System.err.println("Project could not be loaded. " + projectFilePath + " not found."); 
		}
		
		return toLoad;
	}
	
	public void generateChart(Project project, boolean relative)
	{		
		String[] selectedVersions = null;
		ChartVersion[] versionsToChart = null;
		ChartGenerator chartGen = new ChartGenerator();
		
		RocketTools.writeHeader("Generate Charts", headerWidth);
		String[] options = {"FanIn Histogram", "FanOut Histogram"};
		int selection = RocketTools.createMenuAndWait(options);
		
		project.versionListing();			
		// Prompt user to enter the version id's to chart
		selectedVersions = RocketTools.promptParseAndWait("Please enter the versions you wish to chart (comma separated)");		
		versionsToChart = new ChartVersion[selectedVersions.length];
		// Load the appropriate ChartVersion objects
		for(int i=0; i < selectedVersions.length; i++)
		{
			Version selectedVersion = project.getVersion(selectedVersions[i]);
			RocketTools.writeHeader(selectedVersion.getIdentifier() + " Chart Settings", headerWidth);
			int binCount = (int)RocketTools.promptAndWait("Bin count");
			double minBin = RocketTools.promptAndWait("Minimum bin size");
			double maxBin = RocketTools.promptAndWait("Maximum bin size");
			
			versionsToChart[i] = new ChartVersion(selectedVersion, binCount, minBin, maxBin);
			
			//to facilitate the Intention for US7
			//RocketTools.writeToTextFile(versionsToChart[i],"histogram_intention_US7.txt");
		}					
		// Construct a chart using the selected versions
		if (selection == 0)
			chartGen.constructFanInChart(versionsToChart, relative);
		else if (selection == 1)
			chartGen.constructFanOutChart(versionsToChart, relative);
	}	
}
